Skip to content

chore(lexicon): describe every lexicon category - #915

Open
jirhiker wants to merge 1 commit into
stagingfrom
chore/lexicon-descriptions
Open

chore(lexicon): describe every lexicon category#915
jirhiker wants to merge 1 commit into
stagingfrom
chore/lexicon-descriptions

Conversation

@jirhiker

@jirhiker jirhiker commented Sep 1, 2026

Copy link
Copy Markdown
Member

What

61 of the 62 seeded lexicon categories carried description = NULL — only data_maturity had one. This writes the missing text, and gets it into databases that are already seeded.

lexicon_category.description has existed since the initial migration, so nothing here needs a schema change. The text was simply never written.

The descriptions

Most categories are self-evident from their name. The ones worth care are those whose term lists overlap, where the vocabulary is easiest to misread. Grounded in the mapping comments on db/data_provenance.py:

  • collection_method is the union of the legacy NM_Aquifer LU_AltitudeMethod and LU_CoordinateMethod lookups. elevation_method and coordinate_method are those two lookups on their own.
  • origin_type is likewise the union of LU_DataSource and LU_Depth_CompletionSource — both of which also exist as separate categories.
  • casing_material vs screen_type: identical term sets, different well component.
  • status vs the status_type / status_value pair used by status_history.
  • country, county, state have no seeded terms, and say so rather than implying content.

release_status deliberately does not mention embargoed — that term isn't in this branch's lexicon; it arrives with the access-control work and will need a follow-up line then.

All 61 fit the String(255) column. data_maturity was left as-is.

Getting it into existing databases

Two changes, because the JSON alone reaches nothing that's already seeded.

init_lexicon now upserts. It previously inserted only categories that were missing, so a database seeded before the descriptions existed would never receive them. It now uses ON CONFLICT DO UPDATE wrapped in coalesce: a NULL description is filled from the seed, and a description edited through /lexicon is left alone.

A data migration for the populated databases, since neither a fresh seed nor oco initialize-lexicon happens on deploy. It reads the same JSON and touches only NULL descriptions.

It is gated on alembic_revision="66ac1af4ba69" — the initial migration, which created the column. This is deliberate: the runner skips any migration whose revision isn't applied, so pinning to the current head would risk it being silently skipped on an environment whose alembic state lags. The initial revision is applied everywhere.

Testing

  • Two tests added to tests/test_data_migrations.py, following the file's existing pattern: a NULL description gets filled while a hand-edited one survives, and re-running is a no-op.
  • 41 passed across test_data_migrations, test_data_migrations_cli, test_lexicon, test_migrations, test_cli_commands.
  • Full runner path exercised against a live test database, not just run(): the alembic gate passed, descriptions backfilled, a data_migration_history row was written, and a second invocation logged "already applied" and returned False. All 62 categories non-NULL afterward.
  • black + flake8 clean.

Applying it

CD deliberately does not run data migrations — it runs alembic upgrade head and only reports pending ones. So after this merges and deploys, apply it through the Data Migrations workflow (workflow_dispatch), per environment:

  1. action = status against staging — confirms it is registered and unapplied.
  2. action = run with 20260901_0001_backfill_lexicon_category_descriptions.
  3. Repeat for production.

Safe to re-run: it only writes rows whose description is still NULL.

🤖 Generated with Claude Code

61 of the 62 seeded categories carried `description = NULL`; only
`data_maturity` had one. The column has existed since the initial
migration, so nothing needed a schema change -- the text was simply
never written.

Descriptions disambiguate the categories whose term lists overlap, which
is where the vocabulary is easiest to misread. Grounded in the mapping
comments on `db/data_provenance.py`: `collection_method` is the union of
the legacy NM_Aquifer `LU_AltitudeMethod` and `LU_CoordinateMethod`
lookups, while `elevation_method` and `coordinate_method` are those two
lookups on their own; `origin_type` is likewise the union of
`LU_DataSource` and `LU_Depth_CompletionSource`, which also exist
separately. Also separates `casing_material` from `screen_type` (same
terms, different well component) and `status` from the `status_type` /
`status_value` pair used by `status_history`. `country`, `county` and
`state` have no seeded terms, and say so rather than implying content.

`release_status` deliberately does not mention `embargoed`; that term is
not in this branch's lexicon and arrives with the access-control work.

`init_lexicon` inserted only categories that were missing, so a database
seeded before the descriptions existed would never receive them. It now
upserts, wrapped in coalesce: a NULL description is filled from the seed
and a description edited through /lexicon is left alone.

That covers a fresh seed and a re-run of `oco initialize-lexicon`, but
neither happens on deploy, so a data migration backfills the populated
databases. It reads the same JSON, touches only NULL descriptions, and
is gated on `66ac1af4ba69` -- the initial migration, which created the
column. Pinning it to the current head would risk the runner silently
skipping it on an environment whose alembic state lags.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Coverage

79.39% total — gate is 75%.

Coverage for the Python files changed in this PR
Name Stmts Miss Cover Missing
core/initializers.py 142 20 86% 67-69, 73-82, 193, 197, 215, 284-297, 301-302
data_migrations/migrations/20260901_0001_backfill_lexicon_category_descriptions.py 20 0 100%
TOTAL 162 20 88%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant